#include <video.h>
#include <kernel/pcb.h>
#include <kernel/gdt.h>
#include <kernel/sched.h>
#include <kernel/definiciones.h>
#include <kernel/mem/mem_part_firstfit.h>
#include <kernel/mem/mem_part_kernel.h>
#include <kernel/mem/paginas.h>
#include <kernel/system_asm.h>
#include <kernel/syscall.h>
#include <kernel/system.h>
#include <shell/shell.h>
#include <shell/teclado.h>
#include <usr/reloj.h>
#include <usr/sodstring.h>
#include <kernel/semaforo.h>
#include <fs/ramfs.h>
#include <kernel/drivers/dma.h>
#include <kernel/drivers/floppy.h>

unsigned int uiTamanioMemoria;
unsigned int uiTamanioMemoriaBaja;
unsigned int uiTamanioMemoriaBios;
unsigned int uiUnidadBoot;
unsigned int uiTamanioBSS;
unsigned int uiTamanioKernel;
unsigned int uiModoMemoria;


unsigned int uiAPM_OK;

unsigned int uiAPM_CHECK_AX;
unsigned int uiAPM_CHECK_BX;
unsigned int uiAPM_CHECK_CX;

unsigned int uiAPM_32_CS;
unsigned int uiAPM_OFFSET_ENTRY_POINT;
unsigned int uiAPM_16_CS;
unsigned int uiAPM_DS;
extern stuPCB pstuPCB[CANTMAXPROCS];

extern short int bPlanificador;
extern short int bActivarTeclado;


//Puntero a la GDT (Global Descriptor Table)
dword pdwGDT;

//Puntero a la IDT (Interrupt Descriptor Table)
//stuIDT *pstuIdtIDT;

int iFmf, iCmf, iMatrizMf[30][2];
unsigned long ulMemoriaBase = MEM_BASE;
unsigned long ulMemoriaTope = MEM_TOPE;


// void vFnTareaNula (); ahora es proceso usuario
int iFnProcShell ();
int iFnSistema ();
int iFnProceso1 ();
int iFnProcReloj ();
void *pvFnFuncion;

// Funcion para cargar el logueo inicial
void vFnCargarLogueoInicial();

/******************************************************************************
Funcion:
Descripcion:
Recibe:
Devuelve:
*******************************************************************************/
int
main ()
{
  asm ("cli");
  //VARIABLE PARA HABILITAR/DESHABILITAR LOGUEO DEL SODIUM
  usHabilitarLogueo = LOGUEOHABILITADO;
  usCantidadLog = 0;
  
  uliClockTick = 0;
  
  vFnCargarLogueoInicial();
  
  uiHeapKernelInicializado = 0;
  uiKernelStack = 0x200000; // Lo defino temporalmente hasta que se reserve una porcion de memoria para el stack.
  if (uiModoMemoria == MODOPARTICIONFIJA) iModo = FIJO;
  if (uiModoMemoria == MODOVARIABLE) iModo = VARIABLE;
  vFnLogSodium(4, "Inicia la IDT ... [HECHO]");
  vFnIniciarIDT ();
  vFnLogSodium(4, "Inicia teclado ... [HECHO]");
  vFnIniciarTeclado();
  bActivarTeclado = 1;
  vFnLogSodium(4, "Configura Particiones ... [HECHO]");
  vFnConfigurarParticiones();

  vFnImprimir ("\nBienvenidos al SODIUM...\n");
  
  vFnImprimir ("\nModelo de memoria elegido: ");
  if (uiModoMemoria == MODOPAGINADO)
  {
	vFnImprimir ("Paginado	");
	vFnLogSodium(4, "Modelo de memoria elegido: Paginado ... [HECHO]");
  }
  else
  {
	vFnImprimir ("Segmentado	");
	vFnLogSodium(4, "Modelo de memoria elegido: Segmentado ... [HECHO]");
  }
	

  vFnImprimir ("\nActivando Manejador del teclado ...         ");
  vFnImprimirOk (55);
  vFnImprimir ("\nIniciando IDT ...                           ");
  vFnImprimirOk (55);


  vFnImprimir ("\nIniciando los Manejadores de Excepciones ...");
  vFnIniciarExcepciones ();
  vFnImprimirOk (55);

  vFnImprimir ("\nIniciando GDT ...                           ");
  vFnLogSodium(4,"Iniciando GDT ... [HECHO]");
  vFnGdtInicializar (pdwGDT);
  vFnImprimirOk (55);


  vFnImprimir ("\nInicializar Semaforos ...                   ");
  vFnLogSodium(4,"Iniciando Semaforos ... [HECHO]");
  vFnInicializarSemaforos(); /*Inicia el sistema de semaforos*/
  vFnImprimirOk (55);

  vFnImprimir ("\nIniciando Dma ...                           ");
  vFnLogSodium(4, "Iniciando Dma ... [HECHO]");
  inicDMA();
  vFnImprimirOk (55);

  vFnImprimir ("\nIniciando Administrador de Memoria ...                           ");  
  vFnLogSodium(4,"Iniciando Administrador de Memoria ... [HECHO]");
  vFnLog("\nTamanio de Heap de Usuario: 0x%x",100000);
  vFnImprimirOk (55);

  vFnImprimir ("\nIniciando el kernel heap ...                ");
  vFnLogSodium(4,"Iniciando el kernel heap ... [HECHO]");
  vFnInicializarHeapsKernel();
  uiHeapKernelInicializado=1;
  vFnImprimirOk (55);
  
  vFnImprimir ("\nCreando Stack de Kernel ...                ");
  vFnLogSodium(4,"Creando Stack dek Kernel ... [HECHO]");
  uiKernelStack = (pvFnReservarParticionKernel(KERNEL_STACK_SIZE) + KERNEL_STACK_SIZE -4);
  vFnLog("\nPosicion del stack de kernel: 0x%x",uiKernelStack);
  vFnImprimirOk (55);

  vFnImprimir ("\nIniciando los discos RAM ...                ");
  vFnLogSodium(4,"Iniciando los discos RAM ... [HECHO]");
  vFnInicializarPath();

  if (iFnRamFsInit()==0)//iFnIniciarFileSystFloppy()==0)//
	  vFnImprimirOk (55);
  else
	  vFnImprimirNOk(55);




  vFnImprimir ("\nIniciando Tablas de Memoria ...              ");
  vFnLogSodium(4,"Iniciando Tablas de Memoria ... [HECHO]");


  if (uiModoMemoria == MODOPAGINADO)   vFnIniciarMapaBits (ulMemoriaBase, ulMemoriaTope); 	//Paginado
/* La memoria segmentada ya no se simula, por lo que no hace falta esto:
    else
    vFnParticionarMemoria ();	//Segmentado
 */
  vFnImprimirOk (55);




  vFnImprimir ("\nActivando Planificador de tareas (RR) ...   ");
  vFnLogSodium(4,"Iniciando Planificador de tareas (RR) ... [HECHO]");
  lFnSysSchedSetScheduler(RR);
  vFnImprimirOk (55);

  vFnImprimir ("\nSeteando parametros de planificador RR ...  ");
  lFnSysSchedSetParam(QUANTUM);
  uliTimeSlice=0; //inicializo variable para contar tiempos
  vFnImprimirOk (55);


  vFnImprimir ("\nIniciando Tarea Nula ...                    ");
  vFnLogSodium(4,"Iniciando Tarea Nula ... [HECHO]");
  iFnInstanciarIdle();
  vFnImprimirOk (55);

  vFnImprimir ("\nCreando callgate para servicios SODIUM ...  ");
  vFnLogSodium(4,"Creando callgate para servicios SODIUM ... [HECHO]");
  pvFnFuncion = (void *) vFnHandlerCallgate_Asm;
  iFnCrearCallGate(7,(int)pvFnFuncion & 0x0000FFFF, 0x08, 0x0, 0xC, 0xE0, 0x0);
  vFnImprimirOk (55);



  vFnImprimir ("\nInicializar Memoria Compartida...           "); 
  vFnLogSodium(4,"Inicializando Memoria Compartida ... [HECHO]");
  vFnInicializarShms(); /*Inicia el sistema de memoria compartida*/
  vFnImprimirOk (55);



  vFnImprimir ("\nIniciando reloj del sistema ...          ");
  vFnLogSodium(4,"Iniciando reloj del Sistema ... [HECHO]");
  vFnInicializarReloj();
  vFnImprimirOk (55);

  iFnInstanciarReloj(vFnLoopReloj, "PR_Reloj............");
  vFnImprimir ("\nIniciando Proceso Reloj ...                 ");
  vFnLogSodium(4,"Iniciando Proceso Reloj ... [HECHO]");
  vFnImprimirOk (55);

  vFnImprimir ("\nIniciando Task Register ...                 ");
  vFnLogSodium(4,"Iniciando Task Register ... [HECHO]");
  vFnLTR ((unsigned int) pstuPCB[0].uiIndiceGDT_TSS * 8);
  vFnImprimirOk (55);

  vFnImprimir ("\nMemoria total del sistema: %d MB",
	       uiTamanioMemoriaBios / 1024 / 1024);
  vFnImprimir ("\nHabilitando Interrupciones ...              ");
  vFnLogSodium(4,"Habilitando Interrupciones ... [HECHO]");
  vFnImprimirOk (55);

  vFnImprimir ("\nIniciando Proceso Shell ...                 ");
  vFnLogSodium(4,"Iniciando Proceso Shell ... [HECHO]");
  vFnImprimirOk (55);

  
  iFnInstanciarProceso("SHELLUSR.BIN", 3, 0);
  iFnProcShell (0);


  //declaradas en shell.h
  uiAPM_ok = uiAPM_OK;

  uiAPM_check_ax = uiAPM_CHECK_AX;
  uiAPM_check_bx = uiAPM_CHECK_BX;
  uiAPM_check_cx = uiAPM_CHECK_CX;
  
  uiAPM_32_cs = uiAPM_32_CS;
  uiAPM_offset_entry_point = uiAPM_OFFSET_ENTRY_POINT;
  uiAPM_16_cs = uiAPM_16_CS;
  uiAPM_ds = uiAPM_DS;


  asm ("sti");
//vFnMenuLs("/");

  while (1)
    {
    }

}


/**
 * @brief Simula un proceso de sistema
 * @param pid del proceso
 * @date 21/09/2004
 */
int
iFnSistema (int iPid)
{
/*  unsigned long int uliTiempoAlarma = uliClockTick + 200;

  while (1)
    {

      if (uliClockTick > uliTiempoAlarma)
	{
	  uliTiempoAlarma = uliClockTick + 200;
	}
    }
    */
  while (1);

  return (1);
}


/**
 * @brief Simula un proceso del usuario
 * @param pid del proceso
 * @date 21/09/2004
 */
int
iFnProceso1 (int iPid)
{
  unsigned long int uliTiempoAlarma = uliClockTick + 1;
  unsigned long int uliNroBucles = 0;
  int iPCB;

  iPCB=iFnBuscaPosicionProc(iPid);
  while (1)
    {
      if (uliClockTick > uliTiempoAlarma)
	{
	  uliTiempoAlarma = uliClockTick + 1; // Envia un mensaje a pantalla por cada clocktick
	  				      // transcurrido (generalmente son bastantes mas de 1, debido a la multitarea)
	  vFnImprimirVentana (HWND_PROCESOS,
			      "\ngetpid(): %d, getppid(): %d, %s. ClockTicks=%d, MBucles.Seg=%d (aprox)",
			      lFnSysGetPid(),
			      lFnSysGetPPid(),
			      pstuPCB[iPCB].stNombre,
			      uliClockTick,
			      (uliNroBucles * 18 + uliNroBucles / 10 * 2)/1000000);
	  uliNroBucles = 0;
	}
      uliNroBucles++;
    }
  return (1);
}



/***************************************************************************
 Funcion: TareaNula
 Descripcion: Se ejecuta siempre ante la ausencia de otros procesos
		listos para la ejecucion dentro del sistema.
 Parametros: Pid del proceso
 Valor devuelto: Ninguno
 Ultima modificacion: 09/04/2006
***************************************************************************/
/*
void
vFnTareaNula (int iPid)
{
  vFnImprimir ("\nTarea Nula, Pid=%d", iPid);
  vFnImprimirPrompt ();
  while (1);
}
*/ //antigua tarea nula, se mantiene por historia :D
/***************************************************************************
 Funcion: prShell
 Descripcion: El shell corre como un proceso independiente. Ejecuta un
 loop principal que va obteniendo caracteres del buffer del teclado mediante
 un getchar bloqueante en espera activa. A su vez el shell va almacenando
 los caracteres que recupera en su propio buffer, que interpretará luego de
 encontrar el caracter 13.

 Parametros: Pid del proceso
 Valor devuelto: Ninguno

 Ultima modificacion: 09/04/2006
**************************************************************************/
int iFnProcShell (int iPid)
{ 
  if(iFnCambiaTecladoS("us") != 0)
     iFnCambiaTecladoI(1);

  vFnImprimirContexto ();


  return (1);
}

/******************************************************************************
Funcion: vFnCargarLogueoInicial
Descripcion: Separa los logueos iniciales por nivel (bootSector, loader, sodium)
*******************************************************************************/
void vFnCargarLogueoInicial()
{
	char *cMemoriaDato=0x99000;
	int iCaracterLeido = 0;
	
	if(usHabilitarLogueo==LOGUEODESHABILITADO)
		return;
		
	
	for (; iCaracterLeido < 8; iCaracterLeido++)
	{
		switch(iCaracterLeido)
		{
			case 0:
				if (cMemoriaDato[iCaracterLeido] == 'S')
					vFnLogSodium(1,"Finaliza bootSector ... [HECHO]");
				else
					vFnLogSodium(1,"Finaliza bootSector ... [FALLO]");
			break;

			case 1:
				if (cMemoriaDato[iCaracterLeido] == 'S')
					vFnLogSodium(2,"Inicio loader ... [HECHO]");
				else
					vFnLogSodium(2,"Inicio loader ... [FALLO]");
			break;
			
			case 2:
				if (cMemoriaDato[iCaracterLeido] == 'S')
					vFnLogSodium(2,"Finaliza la carga de la FAT, comienza carga de archivos ... [HECHO]");
				else
					vFnLogSodium(2,"Finaliza la carga de la FAT, comienza carga de archivos ... ... [FALLO]");
			break;
			
			case 3:
				if (cMemoriaDato[iCaracterLeido] == 'S')
					vFnLogSodium(2,"Finaliza loader ... [HECHO]");
				else
					vFnLogSodium(2,"Finaliza loader ... [FALLO]");

			break;
			
			case 4:
				if (cMemoriaDato[iCaracterLeido] == 'S')
					vFnLogSodium(3,"Inicio GDT ... [HECHO]");
				else
					vFnLogSodium(3,"Inicio GDT ... [FALLO]");

			break;
			
			case 5:
				if (cMemoriaDato[iCaracterLeido] == 'S')
					vFnLogSodium(3,"Pasaje de modo real a modo protegido ... [HECHO]");
				else
					vFnLogSodium(3,"Pasaje de modo real a modo protegido ... [FALLO]");

			break;
			
			case 6:
				if (cMemoriaDato[iCaracterLeido] == 'S')
					vFnLogSodium(3,"Inicio  main ... [HECHO]");
				else
					vFnLogSodium(3,"Inicio  main ... [FALLO]");

			break;
		}
	}
}